/**
 * 
 */
package de.akabahn.ebd.dbd.impl;

import java.util.HashMap;
import java.util.Map;

import de.akabahn.ebd.dbd.DBDSignal;

/**
 * Class that represents a German signal in the DBD server and that provides
 * constants and method to define and manipulate the German signal aspects.
 * 
 * @author Peter Eimann
 * @since 0.2
 * @version 1.0
 */
public class DBDSignalD extends DBDSignal {

	public static final int Zs3_2 = BIT0;
	public static final int Zs3_3 = BIT1;
	public static final int Zs3_4 = BIT0 + BIT1;
	public static final int Zs3_5 = BIT2;
	public static final int Zs3_6 = BIT2 + BIT0;
	public static final int Zs3_7 = BIT2 + BIT1;
	public static final int Zs3_8 = BIT2 + BIT1 + BIT0;
	public static final int Zs3_9 = BIT3;
	public static final int Zs3_10 = BIT3 + BIT0;
	public static final int Zs3_11 = BIT3 + BIT1;
	public static final int Zs3_12 = BIT3 + BIT1 + BIT0;
	public static final int Zs3_13 = BIT3 + BIT2;
	public static final int Zs3_14 = BIT3 + BIT2 + BIT0;
	public static final int Zs3_15 = BIT3 + BIT2 + BIT1;
	public static final int Zs3_16 = BIT3 + BIT2 + BIT1 + BIT0;

	public static final int Zs3v_2 = BIT4;
	public static final int Zs3v_3 = BIT5;
	public static final int Zs3v_4 = BIT4 + BIT5;
	public static final int Zs3v_5 = BIT6;
	public static final int Zs3v_6 = BIT6 + BIT4;
	public static final int Zs3v_7 = BIT6 + BIT5;
	public static final int Zs3v_8 = BIT6 + BIT5 + BIT4;
	public static final int Zs3v_9 = BIT7;
	public static final int Zs3v_10 = BIT7 + BIT4;
	public static final int Zs3v_11 = BIT7 + BIT5;
	public static final int Zs3v_12 = BIT7 + BIT5 + BIT4;
	public static final int Zs3v_13 = BIT7 + BIT6;
	public static final int Zs3v_14 = BIT7 + BIT6 + BIT4;
	public static final int Zs3v_15 = BIT7 + BIT6 + BIT5;
	public static final int Zs3v_16 = BIT7 + BIT6 + BIT5 + BIT4;

	public static final int Hp0 = BIT8;
	public static final int Ks1 = BIT9;
	public static final int Ks2 = BIT10;
	public static final int Sh1 = BIT11;
	public static final int Hp1 = BIT12;
	public static final int Hp2 = BIT13;
	public static final int Vr0 = BIT14;
	public static final int Vr1 = BIT15;
	public static final int Vr2 = BIT16;

	public static final int Zs1 = BIT17;
	public static final int Zs7 = BIT18;
	public static final int Zs8 = BIT19;

	public static final int Hp_Aus = BIT20;
	public static final int Vr_Aus = BIT21;
	public static final int Ke = BIT22;
	public static final int Zs3_An = BIT23;
	public static final int Zs3v_An = BIT24;
	public static final int HAGT = BIT25;

	private static Map<String, Integer> signalMap;

	static {
		signalMap = new HashMap<String, Integer>();
		signalMap.put("ZS3-2", Zs3_2);
		signalMap.put("ZS3-3", Zs3_3);
		signalMap.put("ZS3-4", Zs3_4);
		signalMap.put("ZS3-5", Zs3_5);
		signalMap.put("ZS3-6", Zs3_6);
		signalMap.put("ZS3-7", Zs3_7);
		signalMap.put("ZS3-8", Zs3_8);
		signalMap.put("ZS3-9", Zs3_9);
		signalMap.put("ZS3-10", Zs3_10);
		signalMap.put("ZS3-11", Zs3_11);
		signalMap.put("ZS3-12", Zs3_12);
		signalMap.put("ZS3-13", Zs3_13);
		signalMap.put("ZS3-14", Zs3_14);
		signalMap.put("ZS3-15", Zs3_15);
		signalMap.put("ZS3-16", Zs3_16);
		signalMap.put("ZS3V-2", Zs3v_2);
		signalMap.put("ZS3V-3", Zs3v_3);
		signalMap.put("ZS3V-4", Zs3v_4);
		signalMap.put("ZS3V-5", Zs3v_5);
		signalMap.put("ZS3V-6", Zs3v_6);
		signalMap.put("ZS3V-7", Zs3v_7);
		signalMap.put("ZS3V-8", Zs3v_8);
		signalMap.put("ZS3V-9", Zs3v_9);
		signalMap.put("ZS3V-10", Zs3v_10);
		signalMap.put("ZS3V-11", Zs3v_11);
		signalMap.put("ZS3V-12", Zs3v_12);
		signalMap.put("ZS3V-13", Zs3v_13);
		signalMap.put("ZS3V-14", Zs3v_14);
		signalMap.put("ZS3V-15", Zs3v_15);
		signalMap.put("ZS3V-16", Zs3v_16);
		signalMap.put("HP0", Hp0);
		signalMap.put("KS1", Ks1);
		signalMap.put("KS2", Ks2);
		signalMap.put("SH1", Sh1);
		signalMap.put("HP1", Hp1);
		signalMap.put("HP2", Hp2);
		signalMap.put("VR0", Vr0);
		signalMap.put("VR1", Vr1);
		signalMap.put("VR2", Vr2);
		signalMap.put("ZS1", Zs1);
		signalMap.put("ZS7", Zs7);
		signalMap.put("ZS8", Zs8);
		signalMap.put("HP-AUS", Hp_Aus);
		signalMap.put("VR-AUS", Vr_Aus);
		signalMap.put("KE", Ke);
		signalMap.put("ZS3-AN", Zs3_An);
		signalMap.put("ZS3V-AN", Zs3v_An);
		signalMap.put("HAGT", HAGT);
	}

	/**
	 * Default constructor
	 */
	public DBDSignalD() {
	}

	/**
	 * Initialize the signal with the signal aspects that correspond to the
	 * passed <code>intValues</code>. For convenience, the provided constants
	 * should be used.
	 * 
	 * @param intValues
	 *            The signal aspects
	 */
	public DBDSignalD(int... intValues) {
		for (int intValue : intValues) {
			this.intValue += intValue;
		}
	}

	/**
	 * Initialize the signal with the signal aspects that correspond to the
	 * passed <code>signalAspects</code>. Each aspect has to match
	 * (case-insensitive) to one of the keys in the <code>signalMap</code>,
	 * otherwise it's ignored.
	 * 
	 * @param signalAspects
	 *            The signal aspects
	 */
	public DBDSignalD(String... signalAspects) {
		for (String signalAspect : signalAspects) {
			if (signalMap.containsKey(signalAspect.toUpperCase())) {
				setBit(signalMap.get(signalAspect.toUpperCase()));
			}
		}
	}

	/**
	 * Set the signal aspects that correspond to the passed
	 * <code>signalAspects</code>. Each aspect has to match (case-insensitive)
	 * to one of the keys in the <code>signalMap</code>, otherwise it's ignored.
	 * 
	 * @param signalAspects
	 *            The signal aspects
	 */
	public void setAspects(String... signalAspects) {
		for (String signalAspect : signalAspects) {
			if (signalMap.containsKey(signalAspect.toUpperCase())) {
				setBit(signalMap.get(signalAspect.toUpperCase()));
			}
		}
	}

	/**
	 * Get the current <code>Zs3</code> value (will be 0 if no Zs3 is set)
	 * 
	 * @return The <code>Zs3</code> value
	 */
	public int getZs3() {
		int zs3 = (intValue & Integer.parseInt("1111", 2)) + 1;
		return zs3 > 1 ? zs3 : 0;
	}

	/**
	 * Get the current <code>Zsv3</code> value (will be 0 if no Zs3v is set)
	 * 
	 * @return The <code>Zs3v</code> value
	 */
	public int getZs3v() {
		int zs3v = ((intValue & Integer.parseInt("11110000", 2)) >> 4) + 1;
		return zs3v > 1 ? zs3v : 0;
	}

	/**
	 * Check if the <code>Zs3</code> is turned on or off
	 * 
	 * @return <code>true</code> - <code>Zs3</code> is turned on,
	 *         <code>false</code> otherwise
	 */
	public boolean isZs3An() {
		return (intValue & Zs3_An) == Zs3_An ? true : false;
	}

	/**
	 * Check if the <code>Zs3v</code> is turned on or off
	 * 
	 * @return <code>true</code> - <code>Zs3v</code> is turned on,
	 *         <code>false</code> otherwise
	 */
	public boolean isZs3vAn() {
		return (intValue & Zs3v_An) == Zs3v_An ? true : false;
	}

	/**
	 * Check if the main signal is turned off or on
	 * 
	 * @return <code>true</code> - main signal is turned off, <code>false</code>
	 *         otherwise
	 */
	public boolean isHpAus() {
		return (intValue & Hp_Aus) == Hp_Aus ? true : false;
	}

	/**
	 * Check if the distant signal is turned off or on
	 * 
	 * @return <code>true</code> - distant signal is turned off,
	 *         <code>false</code> otherwise
	 */
	public boolean isVrAus() {
		return (intValue & Vr_Aus) == Vr_Aus ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Hp0</code>
	 * 
	 * @return <code>true</code> - signal shows <code>Hp0</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isHp0() {
		return (intValue & Hp0) == Hp0 ? true : false;
	}

	/**
	 * Check if the <code>HAGT</code> flag is set
	 * 
	 * @return <code>true</code> - the <code>HAGT</code>, flag is set
	 *         <code>false</code> otherwise
	 */
	public boolean isHAGT() {
		return (intValue & HAGT) == HAGT ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Ks1</code>
	 * 
	 * @return <code>true</code> - signal shows <code>Ks1</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isKs1() {
		return (intValue & Ks1) == Ks1 ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Ks2</code>
	 * 
	 * @return <code>true</code> - signal shows <code>Ks2</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isKs2() {
		return (intValue & Ks2) == Ks2 ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Sh1</code>
	 * 
	 * @return <code>true</code> - signal shows <code>Sh1</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isSh1() {
		return (intValue & Sh1) == Sh1 ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Hp1</code>
	 * 
	 * @return <code>true</code> - signal shows <code>Hp1</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isHp1() {
		return (intValue & Hp1) == Hp1 ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Hp2</code>
	 * 
	 * @return <code>true</code> - signal shows <code>Hp2</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isHp2() {
		return (intValue & Hp2) == Hp2 ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Vr0</code>
	 * 
	 * @return <code>true</code> - signal shows <code>Vr0</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isVr0() {
		return (intValue & Vr0) == Vr0 ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Vr1</code>
	 * 
	 * @return <code>true</code> - signal shows <code>Vr1</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isVr1() {
		return (intValue & Vr1) == Vr1 ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Vr2</code>
	 * 
	 * @return <code>true</code> - signal shows <code>Vr2</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isVr2() {
		return (intValue & Vr2) == Vr2 ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Zs1</code>
	 * 
	 * @return <code>true</code> - signal shows <code>Zs1</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isZs1() {
		return (intValue & Zs1) == Zs1 ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Zs7</code>
	 * 
	 * @return <code>true</code> - signal shows <code>Zs7</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isZs7() {
		return (intValue & Zs7) == Zs7 ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Zs8</code>
	 * 
	 * @return <code>true</code> - signal shows <code>Zs8</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isZs8() {
		return (intValue & Zs8) == Zs8 ? true : false;
	}

	/**
	 * Check if the signal shows the aspect <code>Kennlicht (Ke)</code>
	 * 
	 * @return <code>true</code> - signal shows <code>Ke</code>,
	 *         <code>false</code> otherwise
	 */
	public boolean isKe() {
		return (intValue & Ke) == Ke ? true : false;
	}

	/**
	 * Return a string representation of the current signal aspect.
	 * 
	 * @return The string representation
	 */
	public String toString() {
		StringBuilder buf = new StringBuilder();

		if (getZs3() > 0) {
			buf.append("Zs3\"").append(getZs3()).append("\"");
			buf.append(isZs3An() ? "-An " : "-Aus ");
		}
		if (isHpAus()) {
			buf.append("Hp-Aus ");
		}
		if (isHp0()) {
			buf.append("Hp0 ");
		}
		if (isHAGT()) {
			buf.append("HAGT ");
		}
		if (isKs1()) {
			buf.append("Ks1 ");
		}
		if (isSh1()) {
			buf.append("Sh1 ");
		}
		if (isHp1()) {
			buf.append("Hp1 ");
		}
		if (isHp2()) {
			buf.append("Hp2 ");
		}
		if (isZs1()) {
			buf.append("Zs1 ");
		}
		if (isZs7()) {
			buf.append("Zs7 ");
		}
		if (isZs8()) {
			buf.append("Zs8 ");
		}
		if (isKe()) {
			buf.append("Ke ");
		}
		if (isVrAus()) {
			buf.append("Vr-Aus ");
		}
		if (isVr0()) {
			buf.append("Vr0 ");
		}
		if (isVr1()) {
			buf.append("Vr1 ");
		}
		if (isVr2()) {
			buf.append("Vr2 ");
		}
		if (isKs2()) {
			buf.append("Ks2 ");
		}
		if (getZs3v() > 0) {
			buf.append("Zs3v\"").append(getZs3v()).append("\"");
			buf.append(isZs3vAn() ? "-An " : "-Aus ");
		}

		return buf.toString();
	}
}
